package com.eagle.epay.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eagle.epay.constant.OrderStatusEnum;
import com.eagle.epay.constant.PaymentTypeEnum;
import com.eagle.epay.constant.ResponseEnum;
import com.eagle.epay.entity.Order;
import com.eagle.epay.entity.OrderItem;
import com.eagle.epay.entity.Product;
import com.eagle.epay.entity.Shipping;
import com.eagle.epay.exception.ServiceException;
import com.eagle.epay.form.CartUpdateForm;
import com.eagle.epay.mapper.OrderMapper;
import com.eagle.epay.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eagle.epay.vo.CartProductVo;
import com.eagle.epay.vo.CartVo;
import com.eagle.epay.vo.OrderItemVo;
import com.eagle.epay.vo.OrderVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author eagle
 * @since 2019-12-21
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

	@Autowired
	IShippingService shippingService;

	@Autowired
	IShoppingCartService shoppingCartService;

	@Autowired
	IProductService productService;

	@Autowired
	IOrderItemService orderItemService;

	@Override
	@Transactional
	public OrderVo create(Integer uId, Integer shippingId) {
		//收货地址校验
		QueryWrapper<Shipping> wrapper = new QueryWrapper();
		wrapper.eq("user_id",uId);
		wrapper.eq("id",shippingId);
		Shipping address = shippingService.getOne(wrapper);
		if(null == address){
			throw new ServiceException("The address does not exist");
		}
		//购物车校验(库存等)
		//获得购物车
		List<CartProductVo> cartProductVos = shoppingCartService
				.list(String.valueOf(uId)).getCartProductVoList();
		if(cartProductVos.isEmpty()){
			throw new ServiceException("no product in shopping cart");
		}
		List<CartProductVo> selectedCartProductVos = cartProductVos.stream()
				.filter(CartProductVo::getProductSelected)
				.collect(Collectors.toList());
		if(selectedCartProductVos.isEmpty()){
			throw new ServiceException("no product selected in shopping cart");
		}
		//获得商品详情
		List<Integer> productIds = selectedCartProductVos.stream()
				.map(CartProductVo::getProductId)
				.collect(Collectors.toList());
		QueryWrapper<Product> prodWa = new QueryWrapper();
		prodWa.in("id",productIds);
		List<Product> products = productService.list(prodWa);
		if (products.isEmpty()) {
			throw new ServiceException("the product in shopping cart not matched");
		}
		ConcurrentHashMap<Integer, Product> productMap = products.stream()
				.collect(Collectors.toMap(Product::getId, e -> e, (a, b) -> b, ConcurrentHashMap::new));
		Long orderNo = generateOrderNo();
		List<OrderItem> orderItemList = new ArrayList<>();
		List<Product> productList = new ArrayList<>();
		for (CartProductVo selectedProd : selectedCartProductVos) {
			Product product = productMap.get(selectedProd.getProductId());
			if (product == null) {
				throw new ServiceException("the product "+product.getId()+" does not exists");
			}
			if (product.getStatus() != 1) {
				throw new ServiceException("the product "+product.getId()+" already off salt");
			}
			if(selectedProd.getQuantity()>product.getStock()){
				throw new ServiceException("the product "+product.getId()+" already salt");
			}

			OrderItem orderItem = snapshotOrderItem(uId, orderNo, selectedProd.getQuantity(), product);
			orderItemList.add(orderItem);

			//更新库存
			product.setStock(product.getStock()-selectedProd.getQuantity());
			productList.add(product);
		}

		//创建订单，order和order_item,事务
		Order order = buildOrder(uId, orderNo, shippingId, orderItemList);
		if(this.save(order)){
			if (orderItemService.saveBatch(orderItemList)) {
				log.info("create order successfully");
				//更新库存(减库存)
				productService.updateBatchById(productList);
				//更新购物车
				for (CartProductVo cartProductVo : selectedCartProductVos) {
					CartUpdateForm carUpdateForm = new CartUpdateForm();
					carUpdateForm.setProductId(cartProductVo.getProductId());
					shoppingCartService.deleteCart(String.valueOf(uId), carUpdateForm);
				}
			}else{
				throw new ServiceException("order item created fail");
			}
		}else{
			throw new ServiceException("order created fail");
		}
		//构造VO
		return this.buildOrderVo(order, orderItemList, address);
	}

	private OrderVo buildOrderVo(Order order, List<OrderItem> orderItemList, Shipping address) {
		OrderVo orderVo = new OrderVo();
		BeanUtils.copyProperties(order, orderVo);

		List<OrderItemVo> OrderItemVoList = orderItemList.stream().map(e -> {
			OrderItemVo orderItemVo = new OrderItemVo();
			BeanUtils.copyProperties(e, orderItemVo);
			return orderItemVo;
		}).collect(Collectors.toList());
		orderVo.setOrderItemVoList(OrderItemVoList);

		orderVo.setShippingId(address.getId());
		orderVo.setShippingVo(address);
		return orderVo;
	}

	/**
	 * 企业级：分布式唯一id/主键
	 * @return
	 */
	private Long generateOrderNo() {
		return System.currentTimeMillis() + new Random().nextInt(999);
	}

	private OrderItem snapshotOrderItem(Integer uid, Long orderNo, Integer quantity, Product product){
		OrderItem item = new OrderItem();
		item.setUserId(uid);
		item.setOrderNo(orderNo);
		item.setProductId(product.getId());
		item.setProductName(product.getName());
		item.setProductImage(product.getMainImage());
		item.setCurrentUnitPrice(product.getPrice());
		item.setQuantity(quantity);
		item.setTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
		return item;
	}

	private Order buildOrder(Integer uid,
							 Long orderNo,
							 Integer shippingId,
							 List<OrderItem> orderItemList
	) {
		BigDecimal payment = orderItemList.stream()
				.map(OrderItem::getTotalPrice)
				.reduce(BigDecimal.ZERO, BigDecimal::add);

		Order order = new Order();
		order.setOrderNo(orderNo);
		order.setUserId(uid);
		order.setShippingId(shippingId);
		order.setPayment(payment);
		order.setPaymentType(PaymentTypeEnum.PAY_ONLINE.getCode());
		order.setPostage(0);
		order.setStatus(OrderStatusEnum.NO_PAY.getCode());
		return order;
	}

	@Override
	public PageInfo list(Integer uid, Integer pageNum, Integer pageSize) {
		PageHelper.startPage(pageNum, pageSize);
		QueryWrapper<Order> orderWrapper = new QueryWrapper();
		orderWrapper.eq("user_id",uid);
		List<Order> orders = this.list(orderWrapper);
		List<OrderVo> orderVos = orders.stream().map(s -> {
			OrderVo orderVo = new OrderVo();
			BeanUtils.copyProperties(s, orderVo);
			return orderVo;
		}).collect(Collectors.toList());

		//构造订单明细（订单号唯一，采用了groupingBy）
		List<Long> orderNoList = orders.stream().map(Order::getOrderNo).collect(Collectors.toList());
		QueryWrapper<OrderItem> orderItemWrapper = new QueryWrapper();
		orderItemWrapper.eq("user_id", uid);
		orderItemWrapper.in("order_no", orderNoList);
		List<OrderItem> orderItems = orderItemService.list(orderItemWrapper);
		List<OrderItemVo> orderItemVos = orderItems.stream().map(s -> {
			OrderItemVo orderItemVo = new OrderItemVo();
			BeanUtils.copyProperties(s, orderItemVo);
			return orderItemVo;
		}).collect(Collectors.toList());
		Map<Long, List<OrderItemVo>> orderItemMap = orderItemVos.stream()
				.collect(Collectors.groupingBy(OrderItemVo::getOrderNo));

		//构造地址明细（地址可能重复,因此采用了Collectors.toMap）
		List<Integer> shippingIds = orders.stream().map(Order::getShippingId).collect(Collectors.toList());
		QueryWrapper<Shipping> shippingWrapper = new QueryWrapper();
		shippingWrapper.eq("user_id", uid);
		shippingWrapper.in("id", shippingIds);
		List<Shipping> shippings = shippingService.list(shippingWrapper);
		ConcurrentHashMap<Integer, Shipping> shippingMap = shippings.stream()
				.collect(Collectors.toMap(k -> k.getId(), v -> v, (v1, v2) -> v1, ConcurrentHashMap::new));

		orderVos.forEach(s ->{
			List<OrderItemVo> orderItemData = orderItemMap.get(s.getOrderNo());
			Shipping shipping = shippingMap.get(s.getShippingId());
			s.setOrderItemVoList(orderItemData);
			s.setShippingVo(shipping);
		});

		PageInfo pageInfo = new PageInfo();
		pageInfo.setList(orderVos);
		return pageInfo;
	}

	@Override
	public OrderVo detail(Integer uid, String orderId) {
		QueryWrapper<Order> orderWrapper = new QueryWrapper();
		orderWrapper.eq("user_id",uid);
		orderWrapper.eq("order_no",orderId);
		Order order = this.getOne(orderWrapper);
		OrderVo orderVo = new OrderVo();
		BeanUtils.copyProperties(order, orderVo);

		QueryWrapper<OrderItem> orderItemWrapper = new QueryWrapper();
		orderItemWrapper.eq("user_id", uid);
		orderItemWrapper.eq("order_no", order.getOrderNo());
		List<OrderItem> orderItems = orderItemService.list(orderItemWrapper);
		List<OrderItemVo> orderItemVos = orderItems.stream().map(s -> {
			OrderItemVo orderItemVo = new OrderItemVo();
			BeanUtils.copyProperties(s, orderItemVo);
			return orderItemVo;
		}).collect(Collectors.toList());

		QueryWrapper<Shipping> shippingWrapper = new QueryWrapper();
		shippingWrapper.eq("user_id", uid);
		shippingWrapper.in("id", order.getShippingId());
		Shipping shipping = shippingService.getOne(shippingWrapper);

		orderVo.setOrderItemVoList(orderItemVos);
		orderVo.setShippingVo(shipping);
		orderVo.setShippingId(null != shipping ? shipping.getId():null);
		return orderVo;
	}

	@Override
	public void paid(String orderNo) {
		QueryWrapper<Order> orderWrapper = new QueryWrapper();
		orderWrapper.eq("order_no",orderNo);
		Order order = this.getOne(orderWrapper);

		if (order == null) {
			throw new ServiceException("订单id:" + orderNo + " 不存在");
		}
		//只有[未付款]订单可以变成[已付款]，看自己公司业务
		if (!order.getStatus().equals(OrderStatusEnum.NO_PAY.getCode())) {
			throw new RuntimeException("订单id:" + orderNo + " 状态不正确");
		}

		order.setStatus(OrderStatusEnum.PAID.getCode());
		order.setPaymentTime(LocalDateTime.now());
		boolean result = this.updateById(order);
		if (!result) {
			throw new RuntimeException("将订单更新为已支付状态失败，订单id:" + orderNo);
		}
	}
}
